package org.invigorare.auctions.data;

import java.util.*;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;

/**
 * Abstract superclass for *DataOpeners that need to periodically push/upload new data.
 *
 * The difference between inbound and outbound databases is:
 * 	 - outbound databases only need to track whether each entry has been uploaded.
 *   - inbound databases need to track when each entry was received (to limit uploads)
 *   	Note: when synchronizing inbound data from an external data source, the client will need to 
 *				provided a consistent time frame reference,
 *   			e.g. use the time reported by the server.
 *
 * @author jchern
 */
public abstract class SynchronizableOutboundDataOpener extends SynchronizableDataOpener {

	public static final int FALSE = 0;
	public static final int TRUE = 1;
	
	public static final String _IS_SYNCED = "_synced";
	protected static final String _IS_SYNCED_TYPE = SqlTypes.INT + " DEFAULT " + FALSE; // simulate Bool with 0 or 1
	
	private final String tableName;
	private final String[] rowIdCol = new String[] { _ROW_ID_ };
	
	public SynchronizableOutboundDataOpener(Context context, String tableName, CursorFactory factory, int version) {
		super(context, tableName, factory, version);
		this.tableName = tableName;
	}

	/**
	 * Generates the CREATE TABLE string with the specified column names and types,
	 * automatically appending the _IS_SYNCED column.
	 */
	@Override
	protected String createTableString(String tableName, ColBinding[] columns) {
		ColBinding[] appendedCols = appendColumnDef(columns, new ColBinding(_IS_SYNCED, _IS_SYNCED_TYPE));
		return super.createTableString(tableName, appendedCols);
	}
	
	public List<Integer> getUnsyncedRowIds() {
		SQLiteDatabase db = getReadableDatabase();
		final String selection = _IS_SYNCED + "=" + FALSE;
		Cursor query = db.query(tableName, rowIdCol, selection, null, null, null, null);
		
		List<Integer> rowIds = new ArrayList<Integer>(query.getCount());
		while (query.moveToNext()) {
			rowIds.add(query.getInt(0)); // we queried for rowIdCol, so the one col will give us rowId
		}
		query.close();
		//db.close(); // TODO: close??
		
		return rowIds;
	}
	
	/**
	 * Returns a list of unsynchronized rows, with each row being a map
	 * from the database column name to the value in that column.
	 * @return
	 */
	public List<Map<String, Object>> getUnsyncedRows(String[] columns) {
		List<Map<String, Object>> results = new ArrayList<Map<String, Object>>();
		
		for (String c : columns) {
			Log.i("OutboundDB", c);
		}
		
		SQLiteDatabase db = getReadableDatabase();
		List<Integer> rowIds = getUnsyncedRowIds();
		
		for (int rowId : rowIds) {
			String selection = _ROW_ID_ + "=" + rowId;
			Cursor query = db.query(tableName, columns, selection, null, null, null, null);
			
			if (query.moveToFirst()) {
				Map<String, Object> entry = new HashMap<String, Object>(columns.length + 1);
				entry.put(_ROW_ID_, rowId);
				for (int i = 0; i < columns.length; i++) {
					entry.put(columns[i], query.getString(i)); // TODO: don't force everything to String
				}
				results.add(entry);
			}
			query.close();
		}
		
		db.close();
		return results;
	}

	public void markRowsSent(Collection<Integer> rowIds) {
		
		SQLiteDatabase db = getWritableDatabase();
		
		// column to update
		ContentValues isSyncedTrue = new ContentValues(1);
		isSyncedTrue.put(_IS_SYNCED, TRUE);
		
		// update the isSynced column for each row
		for (int row : rowIds) {
			String whereClause = _ROW_ID_ + "=" + row;
			db.update(tableName, isSyncedTrue, whereClause, null);
			Log.v("OutSyncableDB", "Marking rowId=" + row + " to _IS_SYNCED_ = TRUE");
		}
		
		db.close();
	}
	
}
